﻿

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Text;
using Win32;
using System.Runtime.InteropServices;

namespace QuickJob.forms
{
    public class AlphaBlendTextBox : System.Windows.Forms.TextBox
    {
        #region private variables
        private uPictureBox myPictureBox;
        private bool myUpToDate = false;
        private bool myCaretUpToDate = false;
        private Bitmap myBitmap;
        private Bitmap myAlphaBitmap;
        private int myFontHeight = 10;
        private System.Windows.Forms.Timer myTimer1;
        private bool myCaretState = true;
        private bool myPaintedFirstTime = false;
        private Color myBackColor = Color.White;
        private int myBackAlpha = 10;
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;
        #endregion // end private variables
        #region public methods and overrides
        public AlphaBlendTextBox()
        {
            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();
            // TODO: Add any initialization after the InitializeComponent call
            this.BackColor = myBackColor;
            this.SetStyle(ControlStyles.UserPaint, false);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            myPictureBox = new uPictureBox();
            this.Controls.Add(myPictureBox);
            myPictureBox.Dock = DockStyle.Fill;
            this.ForeColor = Color.Black;
        }
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            this.myBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(this.Width,this.Height);
            this.myAlphaBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(this.Width,this.Height);
            myUpToDate = false;
            this.Invalidate();
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            base.OnMouseClick(e);
        }

        //Some of these should be moved to the WndProc later
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            myUpToDate = false;
            this.Invalidate();
        }
        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            myUpToDate = false;
            this.Invalidate();
        }
        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);
            myUpToDate = false;
            this.Invalidate();
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            this.Invalidate();
        }
        protected override void OnGiveFeedback(GiveFeedbackEventArgs gfbevent)
        {
            base.OnGiveFeedback(gfbevent);
            myUpToDate = false;
            this.Invalidate();
        }
        protected override void OnMouseLeave(EventArgs e)
        {
            //found this code to find the current cursor location
            //at http://www.syncfusion.com/FAQ/WinForms/FAQ_c50c.asp#q597q
            Point ptCursor = Cursor.Position;
            Form f = this.FindForm();
            ptCursor = f.PointToClient(ptCursor);
            if (!this.Bounds.Contains(ptCursor))
                base.OnMouseLeave(e);
        }
        protected override void OnChangeUICues(UICuesEventArgs e)
        {
            base.OnChangeUICues(e);
            myUpToDate = false;
            this.Invalidate();
        }
        //--
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            myCaretUpToDate = false;
            myUpToDate = false;
            this.Invalidate();
            myTimer1 = new System.Windows.Forms.Timer(this.components);
            myTimer1.Interval = (int)User.GetCaretBlinkTime(); //  usually around 500;
            myTimer1.Tick += new EventHandler(myTimer1_Tick);
            myTimer1.Enabled = true;
        }
        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            myCaretUpToDate = false;
            myUpToDate = false;
            this.Invalidate();
            myTimer1.Dispose();
        }
        //--
        protected override void OnFontChanged(EventArgs e)
        {
            if (this.myPaintedFirstTime)
                this.SetStyle(ControlStyles.UserPaint, false);
            base.OnFontChanged(e);
            if (this.myPaintedFirstTime)
                this.SetStyle(ControlStyles.UserPaint, true);
            myFontHeight = GetFontHeight();
            myUpToDate = false;
            this.Invalidate();
        }
        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            myUpToDate = false;
            this.Invalidate();
        }
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            // need to rewrite as a big switch
            if (m.Msg == User.WM_PAINT)
            {
                myPaintedFirstTime = true;
                if (!myUpToDate || !myCaretUpToDate)
                    GetBitmaps();
                myUpToDate = true;
                myCaretUpToDate = true;
                if (myPictureBox.Image != null) myPictureBox.Image.Dispose();
                myPictureBox.Image = (Image)myAlphaBitmap.Clone();
            }
            else if (m.Msg == User.WM_HSCROLL || m.Msg == User.WM_VSCROLL)
            {
                myUpToDate = false;
                this.Invalidate();
            }
            else if (m.Msg == User.WM_LBUTTONDOWN
            || m.Msg == User.WM_RBUTTONDOWN
            || m.Msg == User.WM_LBUTTONDBLCLK
                  //|| m.Msg == User.WM_MOUSELEAVE  ///****
            )
            {
                if (m.Msg == User.WM_LBUTTONDBLCLK)
                {
                    m.Msg = m.Msg;
                }
                myUpToDate = false;
                this.Invalidate();
            }
            else if (m.Msg == User.WM_MOUSEMOVE)
            {
                if (m.WParam.ToInt32() != 0)  //shift key or other buttons
                {
                    myUpToDate = false;
                    this.Invalidate();
                }
            }

            System.Diagnostics.Debug.WriteLine("Pro: " + m.Msg.ToString("X"));
        }




        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                //this.BackColor = Color.Pink;
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }
        #endregion	//end public method and overrides



        #region public property overrides
        public new BorderStyle BorderStyle
        {
            get { return base.BorderStyle; }
            set
            {
                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, false);
                base.BorderStyle = value;
                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, true);
                this.myBitmap = null;
                this.myAlphaBitmap = null;
                myUpToDate = false;
                this.Invalidate();
            }
        }
        public new Color BackColor
        {
            get
            {
                return Color.FromArgb(base.BackColor.R, base.BackColor.G, base.BackColor.B);
            }
            set
            {
                myBackColor = value;
                base.BackColor = value;
                myUpToDate = false;
            }
        }
        public override bool Multiline
        {
            get { return base.Multiline; }
            set
            {
                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, false);
                base.Multiline = value;
                if (this.myPaintedFirstTime)
                    this.SetStyle(ControlStyles.UserPaint, true);
                this.myBitmap = null;
                this.myAlphaBitmap = null;
                myUpToDate = false;
                this.Invalidate();
            }
        }
        #endregion    //end public property overrides


        #region private functions and classes
        private int GetFontHeight()
        {
            Graphics g = this.CreateGraphics();
            SizeF sf_font = g.MeasureString("X", this.Font);
            g.Dispose();
            return (int)sf_font.Height;
        }
        private void GetBitmaps()
        {
            if (myBitmap == null
            || myAlphaBitmap == null
            || myBitmap.Width != Width
            || myBitmap.Height != Height
            || myAlphaBitmap.Width != Width
            || myAlphaBitmap.Height != Height)
            {
                myBitmap = null;
                myAlphaBitmap = null;
            }
            if (myBitmap == null)
            {
                myBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(Width,Height);
                myUpToDate = false;
            }
            if (!myUpToDate)
            {
                //Capture the TextBox control window
                this.SetStyle(ControlStyles.UserPaint, false);
                CaptureWindow(this, ref myBitmap);
                this.SetStyle(ControlStyles.UserPaint, true);
                this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
                this.BackColor = Color.FromArgb(myBackAlpha, myBackColor);
            }
            //--
            Rectangle r2 = new Rectangle(0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            ImageAttributes tempImageAttr = new ImageAttributes();
            //Found the color map code in the MS Help
            ColorMap[] tempColorMap = new ColorMap[1];
            tempColorMap[0] = new ColorMap();
            tempColorMap[0].OldColor = Color.FromArgb(255, myBackColor);
            tempColorMap[0].NewColor = Color.FromArgb(myBackAlpha, myBackColor);
            tempImageAttr.SetRemapTable(tempColorMap);
            if (myAlphaBitmap != null)
                myAlphaBitmap.Dispose();
            myAlphaBitmap = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);//(Width,Height);
            Graphics tempGraphics1 = Graphics.FromImage(myAlphaBitmap);
            tempGraphics1.DrawImage(myBitmap, r2, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height, GraphicsUnit.Pixel, tempImageAttr);
            tempGraphics1.Dispose();
            //----
            if (this.Focused && (this.SelectionLength == 0))
            {
                Graphics tempGraphics2 = Graphics.FromImage(myAlphaBitmap);
                if (myCaretState)
                {
                    try
                    {
                        int index = this.GetFirstCharIndexOfCurrentLine();//得到当前行第一个字符的索引

                        int line = this.GetLineFromCharIndex(index) + 1;//得到当前行的行号,从0开始，习惯是从1开始，所以+1.

                        int col = this.SelectionStart - index + 1;//.SelectionStart得到光标所在位置的索引 减去 当前行第一个字符

                        if (line >= 0 && col >= 0 && line <= this.Lines.Length)
                        {
                            String str= this.Lines[line - 1].Substring(0, col - 1);
                            //SizeF sizeF = tempGraphics2.MeasureString(str, Font);
                            //SizeF sizeF = TextRenderer.MeasureText(str, this.Font);
                            //SizeF sizeF = tempGraphics2.MeasureString(str, Font, 1000, StringFormat.GenericTypographic);
                            SizeF sizeF = TextRenderer.MeasureText(tempGraphics2, str, Font, new Size(0, 0), TextFormatFlags.NoPadding);
                            if (sizeF.Height == 0) sizeF.Height = Font.Height;

                            Pen p = new Pen(this.ForeColor, 3);
                            tempGraphics2.DrawLine(p, (sizeF.Width), (sizeF.Height * (line - 1)) + 0, (sizeF.Width), (sizeF.Height * (line - 1)) + myFontHeight);
                        }

                    }
                    catch { }
                    ////Draw the caret
                    //Point caret = this.findCaret();
                    ////Point caret = new Point(this.SelectionStart);
                    //Pen p = new Pen(this.ForeColor, 3);
                    ////SizeF sizeF = tempGraphics2.MeasureString(Text.Substring(0,caret.X), Font);
                    ////int X = (int)sizeF.Width;
                    //tempGraphics2.DrawLine(p, caret.X, caret.Y + 0, caret.X, caret.Y + myFontHeight);
                   // tempGraphics2.DrawLine(p, X, caret.Y + 0, X, caret.Y + myFontHeight);
                    tempGraphics2.Dispose();
                }
            }
        }
        private Point findCaret()
        {
            /*  Find the caret translated from code at 
             * http://www.vb-helper.com/howto_track_textbox_caret.html
             * 
             * and 
             * 
             * http://www.microbion.co.uk/developers/csharp/textpos2.htm
             * 
             * Changed to EM_POSFROMCHAR
             * 
             * This code still needs to be cleaned up and debugged
             * */
            Point pointCaret = new Point(0);
            int i_char_loc = this.SelectionStart;
            IntPtr pi_char_loc = new IntPtr(i_char_loc);
            //SendMessage(HWND hwnd, int wMsg, int wParam, IntPtr lParam);
            int i_point = User.SendMessage(this.Handle, User.EM_POSFROMCHAR, (int)pi_char_loc, IntPtr.Zero);
            //pointCaret = new Point(i_point);
            pointCaret = new Point(i_char_loc);
            if (i_char_loc == 0)
            {
                pointCaret = new Point(0);
            }
            else if (i_char_loc >= this.Text.Length)
            {
                pi_char_loc = new IntPtr(i_char_loc - 1);
                i_point = User.SendMessage(this.Handle, User.EM_POSFROMCHAR, (int)pi_char_loc, IntPtr.Zero);
                pointCaret = new Point(i_point);
                Graphics g = this.CreateGraphics();
                String t1 = this.Text.Substring(this.Text.Length - 1, 1) + "X";
                SizeF sizet1 = g.MeasureString(t1, this.Font);
                SizeF sizex = g.MeasureString("X", this.Font);
                g.Dispose();
                int xoffset = (int)(sizet1.Width - sizex.Width);
                pointCaret.X = pointCaret.X + xoffset;
                if (i_char_loc == this.Text.Length)
                {
                    String slast = this.Text.Substring(Text.Length - 1, 1);
                    if (slast == "n")
                    {
                        pointCaret.X = 1;
                        pointCaret.Y = pointCaret.Y + myFontHeight;
                    }
                }
            }
            return pointCaret;
        }
        private void myTimer1_Tick(object sender, EventArgs e)
        {
            //Timer used to turn caret on and off for focused control
            myCaretState = !myCaretState;
            myCaretUpToDate = false;
            this.Invalidate();
        }
        private class uPictureBox : PictureBox
        {
            public uPictureBox()
            {
                this.SetStyle(ControlStyles.Selectable, false);
                this.SetStyle(ControlStyles.UserPaint, true);
                this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
                this.SetStyle(ControlStyles.DoubleBuffer, true);
                this.Cursor = null;
                this.Enabled = true;
                this.SizeMode = PictureBoxSizeMode.Normal;
            }
            //uPictureBox
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == User.WM_LBUTTONDOWN
                || m.Msg == User.WM_RBUTTONDOWN
                || m.Msg == User.WM_LBUTTONDBLCLK
                || m.Msg == User.WM_MOUSELEAVE
                || m.Msg == User.WM_MOUSEMOVE)
                {
                    //Send the above messages back to the parent control
                    User.PostMessage(this.Parent.Handle, (int)m.Msg, (int)m.WParam, (int)m.LParam);
                }
                else if (m.Msg == User.WM_LBUTTONUP)
                {
                    //??  for selects and such
                    this.Parent.Invalidate();
                }
                base.WndProc(ref m);
            }
        }   // End uPictureBox Class
        #endregion  // end private functions and classes



        #region Component Designer generated code  InitializeComponent()
        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion





        #region New Public Properties   BackAlpha
        [
        Category("Appearance"),
        Description("The alpha value used to blend the control's background. Valid values are 0 through 255."),
        Browsable(true),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
        ]
        public int BackAlpha
        {
            get { return myBackAlpha; }
            set
            {
                int v = value;
                if (v > 255)
                    v = 255;
                myBackAlpha = v;
                myUpToDate = false;
                Invalidate();
            }
        }
        #endregion





        #region capture windows

        [DllImport("USER32.DLL", EntryPoint = "GetCaretBlinkTime")]
        public static extern uint GetCaretBlinkTime();
        const int WM_PRINTCLIENT = 0x0318;
        const long PRF_CHECKVISIBLE = 0x00000001L;
        const long PRF_NONCLIENT = 0x00000002L;
        const long PRF_CLIENT = 0x00000004L;
        const long PRF_ERASEBKGND = 0x00000008L;
        const long PRF_CHILDREN = 0x00000010L;
        const long PRF_OWNED = 0x00000020L;
        /*  Will clean this up later doing something like this
        enum  CaptureOptions : long
        {
                PRF_CHECKVISIBLE= 0x00000001L,
                PRF_NONCLIENT   = 0x00000002L,
                PRF_CLIENT              = 0x00000004L,
                PRF_ERASEBKGND  = 0x00000008L,
                PRF_CHILDREN    = 0x00000010L,
                PRF_OWNED               = 0x00000020L
        }
        */
        public static bool CaptureWindow(System.Windows.Forms.Control control,
                                                        ref System.Drawing.Bitmap bitmap)
        {
            //This function captures the contents of a window or control

            Graphics g2 = Graphics.FromImage(bitmap);

            //PRF_CHILDREN // PRF_NONCLIENT
            int meint = (int)(PRF_CLIENT | PRF_ERASEBKGND); //| PRF_OWNED ); //  );
            System.IntPtr meptr = new System.IntPtr(meint);

            System.IntPtr hdc = g2.GetHdc();
            User.SendMessage(control.Handle, (int)User.WM_PRINT, (int)hdc, meptr);

            g2.ReleaseHdc(hdc);
            g2.Dispose();

            return true;

        }


        #endregion
    }  // End AlphaTextBox Class

}
